package eval;

import com.google.common.collect.Sets;
import config.Config;
import earlyDetection.EarlyDetector;
import earlyDetection.Setting;
import network.Device;
import network.Network;
import network.Port;
import network.Rule;
import networkLoader.FBNetwork;
import networkLoader.I2Network;
import partition.SubspacePartition;
import verifier.JiffyVerifier;
import verifier.Ports.PersistentPorts;
import verifier.Ports.Ports;
import verifier.util.Changes;
import verifier.util.PPM;

import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class I2EarlyDetectionEval {
    public static int sumClosed = 0;
    public static long sumEDTime = 0;
    public static int sumProcessedRules = 0;

    public static void main(String[] args) throws InterruptedException {
        singleThreadRandom();
        System.exit(1);
        Network network = I2Network.getNetwork();
        for (Device device : network.getAllDevices()) {
            device.getInitialRules().get(device.getInitialRules().size() - 1).setLast(true);
        }

        for (int i = 2; i < 9; i++) {
            System.out.println("#dampping: " + i);
            HashSet<Device> allDevices = new HashSet<>(network.getAllDevices());
            Map<List<Rule>, Long> rulesToInterval = new HashMap<>();
            for (Device device : network.getAllDevices()) {
                rulesToInterval.put(device.getInitialRules(), (long) 0);
            }
            Set<Set<Device>> combinations = Sets.combinations(network.getAllDevices(), i);
            for (Set<Device> devices : combinations) {
//                for (Device device : devices) {
//                    rulesToInterval.put(device.getInitialRules(), (long) 60000);
//                }
                allDevices.removeAll(devices);
                for (Device device : allDevices) {
                    rulesToInterval.put(device.getInitialRules(), (long) 0);
                }

                run(network, rulesToInterval);
                TimeUnit.SECONDS.sleep(1);
            }
        }


//        System.out.println("#avg Closed: " + (sumClosed / 5));
//        System.out.println("#avg Rules: " + (sumProcessedRules / 5));
//        System.out.println("#avg EDTime: " + (sumEDTime / 5));
    }

    private static void run(Network network, Map<List<Rule>, Long> rulesToInterval) {
        BlockingQueue<Rule> blockingQueue = new LinkedBlockingQueue<>();
        EventEmiter eventEmiter = new EventEmiter(blockingQueue);
        for (Map.Entry<List<Rule>, Long> entry : rulesToInterval.entrySet()) {
            eventEmiter.addRulesToInterval(entry.getKey(), entry.getValue());
        }

        MCI mci = new MCI(network, blockingQueue);
        mci.setEnableEarlyDetection(true);
        new Thread(mci).start();
        new Thread(eventEmiter).start();
    }

    private static void singleThread() throws InterruptedException {
        Network network = I2Network.getNetwork();
        for (Device device : network.getAllDevices()) {
            device.getInitialRules().get(device.getInitialRules().size() - 1).setLast(true);
        }

        for (int i = 1; i < 9; i++) {
            System.out.println("#dampping: " + i);
            Set<Set<Device>> combinations = Sets.combinations(network.getAllDevices(), i);
            System.out.println("#cases: " + combinations.size());
            int caseCnt = 1;
            for (Set<Device> devices : combinations) {
                HashSet<Device> unDamppingDevices = new HashSet<>(network.getAllDevices());
                JiffyVerifier verifier = new JiffyVerifier(network);
                EarlyDetector earlyDetector = new EarlyDetector();
                unDamppingDevices.removeAll(devices);
                List<Device> shuffled = new ArrayList<>(unDamppingDevices);
//                Collections.shuffle(shuffled);
                long startAt = System.nanoTime();
                Config.startAt = startAt;
                int nClosed = 0;
                for (Device device : shuffled) {
                    Changes changes = verifier.insertMiniBatch(device.getInitialRules());
                    verifier.update(changes);
                    nClosed++;
                    if (nClosed > -1) {
                        Setting setting = new Setting(i, caseCnt, startAt);
//                        earlyDetector.detectLoop(setting, network, new HashSet<>(Arrays.asList(device)), (HashMap<Ports, Integer>) verifier.portsToPredicate.clone());
                    }
                }
                caseCnt++;
                System.gc();
//                TimeUnit.MILLISECONDS.sleep(100);
            }
        }
    }

    private static void singleThreadRandom() {
        Network network = I2Network.getNetwork();
        for (Device device : network.getAllDevices()) {
            device.getInitialRules().get(device.getInitialRules().size() - 1).setLast(true);
        }

        for (int i = 1; i < 8; i++) {
            System.out.println("#dampping: " + i);
            for (int cnt = 0; cnt < 100; cnt++) {
                List<Device> shuffled = new ArrayList<>(network.getAllDevices());
                Collections.shuffle(shuffled);
                List<Device> remains = shuffled.subList(0, 9 - i);

                JiffyVerifier verifier = new JiffyVerifier(network);
                EarlyDetector earlyDetector = new EarlyDetector();
                long startAt = System.nanoTime();
                for (Device device : remains) {
                    Changes changes = verifier.insertMiniBatch(device.getInitialRules());
                    verifier.update(changes);
                    Setting setting = new Setting(i, cnt, startAt);
                    earlyDetector.detectLoop(setting, network, new HashSet<>(Arrays.asList(device)), verifier.getPortToPredicate());
                }
                System.gc();


            }
        }
    }
}